# 我想到一个方案了，可以这样做
# 1.既然是写长篇小说，就该使用多轮对话
# 2.不可能每次都把小说完整的交给deepseek，这样马上就会超过最大字数限制的。可以将整篇小说分成一个个阶段，每个阶段交给deepseek的提示词都将包含之前内容的概括和当前阶段需要写的内容。
# 3.小说的大纲需要在每轮对话的时候都写在提示词中，防止deepseek忘记
# 4.每轮对话的提示词还应当包括当前的进度
# 5.用户可以自行决定每轮对话中deepseek写的内容是否接受，不接受的话用户可以提出修改，直到用户对deepseek写的内容满意为止，方可进行下一阶段
# 6.每个阶段结束后将内容追加到.md文件中，表示当前已经写的小说的部分
# 这个方案最终交给deepseek的提示词会比较复杂，请你根据这几点方案，并参考下面这个文章生成器来编写最终的程序
import sys
import os
import traceback
import logging
from logging.handlers import RotatingFileHandler
import re

from PyQt5.QtGui import QTextCursor
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QLabel, QLineEdit, QTextEdit,
    QPushButton, QSpinBox, QMessageBox, QProgressBar
)
from PyQt5.QtCore import Qt, QThread, pyqtSignal
from openai import OpenAI  # 假设 deepseek 使用 OpenAI SDK 接口格式

# 日志配置
log_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
log_handler = RotatingFileHandler('novel_generator.log', maxBytes=1 * 1024 * 1024, backupCount=3)
log_handler.setFormatter(log_formatter)
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
logger.addHandler(log_handler)

API_KEY = os.getenv("DEEPSEEK_API_KEY")
API_BASE_URL = os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com")
MODEL_NAME = os.getenv("DEESEEK_MODEL_NAME", "deepseek-reasoner")

SUMMARY_FILENAME = "novel_summary.md"
NOVEL_FILENAME = "novel_draft.md"

def sanitize_filename(filename: str) -> str:
    return re.sub(r'[\\/:*?"<>|]', '_', filename)

class NovelWorker(QThread):
    progress_signal = pyqtSignal(str)
    finished_signal = pyqtSignal(str)
    error_signal = pyqtSignal(str)

    def __init__(self, prompt, api_client, parent=None):
        super().__init__(parent)
        self.prompt = prompt
        self.api_client = api_client

    def run(self):
        messages = [{"role": "user", "content": self.prompt}]
        full_response = ""
        try:
            response = self.api_client.chat.completions.create(
                model=MODEL_NAME,
                messages=messages,
                stream=True
            )
            for chunk in response:
                if hasattr(chunk.choices[0].delta, 'content') and chunk.choices[0].delta.content:
                    content_part = chunk.choices[0].delta.content
                    full_response += content_part
                    self.progress_signal.emit(content_part)
            self.finished_signal.emit(full_response)
        except Exception as e:
            error_message = f"生成内容时出错: {str(e)}\n{traceback.format_exc()}"
            logger.error(error_message)
            self.error_signal.emit(error_message)

class NovelGeneratorApp(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("小说生成器")
        self.setGeometry(200, 200, 700, 500)
        self.initUI()

        self.client = OpenAI(api_key=API_KEY, base_url=API_BASE_URL)
        self.novel_worker = None
        self.summary_worker = None

        self.current_prompt = ""
        self.last_generated_text = ""

    def initUI(self):
        layout = QVBoxLayout()

        self.title_input = QLineEdit(self)
        self.title_input.setPlaceholderText("小说标题")
        layout.addWidget(self.title_input)

        self.outline_input = QTextEdit(self)
        self.outline_input.setPlaceholderText("小说大纲")
        layout.addWidget(self.outline_input)

        self.chapter_input = QLineEdit(self)
        self.chapter_input.setPlaceholderText("当前章节标题")
        layout.addWidget(self.chapter_input)

        self.goal_input = QTextEdit(self)
        self.goal_input.setPlaceholderText("本章节目标/内容要点")
        layout.addWidget(self.goal_input)

        self.generate_button = QPushButton("生成章节", self)
        self.generate_button.clicked.connect(self.generate_chapter)
        layout.addWidget(self.generate_button)

        self.retry_button = QPushButton("重新生成当前章节", self)
        self.retry_button.clicked.connect(self.retry_generation)
        layout.addWidget(self.retry_button)

        self.accept_button = QPushButton("接受并保存章节", self)
        self.accept_button.clicked.connect(self.accept_and_save)
        layout.addWidget(self.accept_button)

        self.progress_bar = QProgressBar(self)
        self.progress_bar.setRange(0, 0)
        self.progress_bar.setVisible(False)
        layout.addWidget(self.progress_bar)

        self.output_text = QTextEdit(self)
        self.output_text.setReadOnly(True)
        layout.addWidget(self.output_text)

        self.setLayout(layout)

    def generate_chapter(self):
        title = self.title_input.text().strip()
        outline = self.outline_input.toPlainText().strip()
        chapter_title = self.chapter_input.text().strip()
        goal = self.goal_input.toPlainText().strip()

        if not title or not outline or not chapter_title or not goal:
            QMessageBox.warning(self, "提示", "请填写完整信息")
            return

        summary = self.load_summary()
        prompt = (
            f"你正在协助创作一本小说，标题为：{title}\n"
            f"小说大纲：\n{outline}\n"
            f"当前进度的概括：\n{summary}\n"
            f"请继续写作下一个章节：{chapter_title}，目标是：{goal}"
        )
        self.current_prompt = prompt

        self.output_text.clear()
        self.progress_bar.setVisible(True)

        self.novel_worker = NovelWorker(prompt, self.client)
        self.novel_worker.progress_signal.connect(self.append_output)
        self.novel_worker.finished_signal.connect(self.save_last_generated)
        self.novel_worker.error_signal.connect(self.show_error)
        self.novel_worker.start()

    def retry_generation(self):
        if not self.current_prompt:
            QMessageBox.warning(self, "提示", "请先生成一次章节内容")
            return
        self.output_text.clear()
        self.progress_bar.setVisible(True)

        self.novel_worker = NovelWorker(self.current_prompt, self.client)
        self.novel_worker.progress_signal.connect(self.append_output)
        self.novel_worker.finished_signal.connect(self.save_last_generated)
        self.novel_worker.error_signal.connect(self.show_error)
        self.novel_worker.start()

    def accept_and_save(self):
        if not self.last_generated_text:
            QMessageBox.warning(self, "提示", "没有内容可以保存")
            return

        with open(NOVEL_FILENAME, "a", encoding="utf-8") as f:
            f.write(self.last_generated_text + "\n\n")

        summary_prompt = (
            f"你正在协助创作一本小说，请根据已有概括内容和新章节内容，更新概括：\n"
            f"原概括：\n{self.load_summary()}\n"
            f"新章节内容：\n{self.last_generated_text}"
        )

        self.summary_worker = NovelWorker(summary_prompt, self.client)
        self.summary_worker.finished_signal.connect(self.update_summary)
        self.summary_worker.error_signal.connect(self.show_error)
        self.summary_worker.start()

        QMessageBox.information(self, "保存成功", "章节已保存并将更新概括")

    def append_output(self, text):
        cursor = self.output_text.textCursor()
        cursor.movePosition(QTextCursor.End)
        cursor.insertText(text)
        self.output_text.setTextCursor(cursor)
        self.output_text.ensureCursorVisible()

    def save_last_generated(self, full_text):
        self.last_generated_text = full_text
        self.progress_bar.setVisible(False)

    def update_summary(self, updated_summary):
        with open(SUMMARY_FILENAME, "w", encoding="utf-8") as f:
            f.write(updated_summary)

    def load_summary(self):
        if os.path.exists(SUMMARY_FILENAME):
            with open(SUMMARY_FILENAME, "r", encoding="utf-8") as f:
                return f.read()
        return "(尚无内容)"

    def show_error(self, message):
        self.progress_bar.setVisible(False)
        QMessageBox.critical(self, "出错了喵", message)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = NovelGeneratorApp()
    window.show()
    sys.exit(app.exec_())
